package org.dromara.patrol.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.enums.NoticeMessageType;
import org.dromara.common.core.enums.patrol.MainTaskType;
import org.dromara.common.core.enums.patrol.TaskFinishStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.patrol.TaskException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.patrol.domain.PatrolSubtaskCommon;
import org.dromara.patrol.domain.model.ExecuteRule;
import org.dromara.patrol.domain.model.Executors;
import org.dromara.patrol.domain.model.PatrolExecutors;
import org.dromara.common.core.enums.patrol.RepeatType;
import org.dromara.patrol.mapper.PatrolSubtaskCommonMapper;
import org.dromara.patrol.service.IPatrolSubtaskCommonService;
import org.dromara.system.api.RemoteMessageService;
import org.dromara.system.api.domain.bo.RemoteMessageBo;
import org.springframework.stereotype.Service;
import org.dromara.patrol.domain.bo.PatrolTaskBo;
import org.dromara.patrol.domain.vo.PatrolTaskVo;
import org.dromara.patrol.domain.PatrolTask;
import org.dromara.patrol.mapper.PatrolTaskMapper;
import org.dromara.patrol.service.IPatrolTaskService;
import tech.powerjob.client.PowerJobClient;
import tech.powerjob.common.request.http.SaveJobInfoRequest;
import tech.powerjob.common.model.LifeCycle;
import tech.powerjob.common.response.ResultDTO;

import java.util.*;
import java.util.stream.Collectors;

import static tech.powerjob.common.enums.ExecuteType.STANDALONE;
import static tech.powerjob.common.enums.ProcessorType.BUILT_IN;
import static tech.powerjob.common.enums.TimeExpressionType.CRON;

/**
 * 巡店管理-巡店任务（实时、现场、录像，不包含自检任务）配置Service业务层处理
 *
 * @author Chan
 * @date 2024-03-21
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class PatrolTaskServiceImpl implements IPatrolTaskService {

    private final PowerJobClient powerJobClient;

    private final PatrolTaskMapper baseMapper;

    private final PatrolSubtaskCommonMapper patrolSubtaskCommonMapper;

    private final IPatrolSubtaskCommonService patrolSubtaskCommonService;

    @DubboReference
    private RemoteMessageService remoteMessageService;

    /**
     * 根据任务类型查询最新一条任务信息
     * @param taskSubType
     * @return
     */
    @Override
    public PatrolTaskVo queryLatelyTaskInfo(String taskSubType) {
        PatrolTaskVo patrolTaskVo = baseMapper.selectVoOne(new LambdaQueryWrapper<>(PatrolTask.class)
//            .eq(PatrolTask::getTenantId, LoginHelper.getTenantId())
            .eq(PatrolTask::getTaskSubType, taskSubType)
            .eq(PatrolTask::getCreator, LoginHelper.getUserId())
            .orderByDesc(PatrolTask::getCreatedTime)
            .last("limit 1"));
        return patrolTaskVo;
    }

    /**
     * 查询巡店管理-巡店任务（实时、现场、录像，不包含自检任务）配置
     */
    @Override
    public PatrolTaskVo queryById(Long taskId){
        PatrolTaskVo vo = baseMapper.selectVoById(taskId);
        return vo;
    }

    /**
     * 查询巡店管理-巡店任务（实时、现场、录像，不包含自检任务）配置列表
     */
    @Override
    public TableDataInfo<PatrolTaskVo> queryPageList(PatrolTaskBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<PatrolTask> lqw = buildQueryWrapper(bo);
        Page<PatrolTaskVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询巡店管理-巡店任务（实时、现场、录像，不包含自检任务）配置列表
     */
    @Override
    public List<PatrolTaskVo> queryList(PatrolTaskBo bo) {
        LambdaQueryWrapper<PatrolTask> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<PatrolTask> buildQueryWrapper(PatrolTaskBo bo) {
        Map<String, Object> params = bo.getParams();
        return Wrappers.lambdaQuery();
    }

    /**
     * 新增巡店管理-巡店任务（实时、现场、录像，不包含自检任务）配置
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Boolean insertByBo(PatrolTaskBo bo) {
        PatrolTask add = buildPatrolTask(bo);
        // 创建任务记录
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            // 创建主子任务记录
            bo.setTaskId(add.getTaskId());
            sendMessage(bo);
            return insertLogic(bo, add);
        }
        throw new ServiceException("新增任务失败", HttpStatus.WARN);
    }

    /**
     * 添加消息通知
     * @param bo
     */
    private void sendMessage(PatrolTaskBo bo) {
        // 执行人
        List<PatrolExecutors> patrolExecutors = bo.getPatrolExecutors();
        if (CollUtil.isEmpty(patrolExecutors)) {
            return;
        }
        List<RemoteMessageBo> handlerMessageList = new ArrayList<>();
        for (PatrolExecutors patrolExecutor : patrolExecutors) {
            //门店
            List<Executors> executors = patrolExecutor.getExecutors();
            for (Executors executor : executors) {
                //门店下面需要处理的人
                handlerMessageList.add(createRemoteMessageBo(executor, patrolExecutor));
            }
        }
        //根据接收人id,转化成map集合
        Map<Long, List<RemoteMessageBo>> map = handlerMessageList.stream().
                collect(Collectors.groupingBy(RemoteMessageBo::getRecordId));
        handlerMessageList.clear();
        map.forEach((k, v) -> {
            RemoteMessageBo remoteMessageBo = new RemoteMessageBo();
            remoteMessageBo.setType(NoticeMessageType.NOTICE_RECEIVE_TASK);
            remoteMessageBo.setMessageContent( NoticeMessageType.NOTICE_RECEIVE_TASK.getAlert().formatted(v.size()));
            remoteMessageBo.setTitle(NoticeMessageType.NOTICE_RECEIVE_TASK.getTitle());
            remoteMessageBo.setStatus("0");
            remoteMessageBo.setSendId(LoginHelper.getUserId());
            remoteMessageBo.setRecordId(k);
            remoteMessageBo.setUrl("/inspectionManagement/task/taskList");
            handlerMessageList.add(remoteMessageBo);
        });
        remoteMessageService.insertBatchByBo(handlerMessageList);
    }

    private RemoteMessageBo createRemoteMessageBo(Executors executor, PatrolExecutors patrolExecutor) {
            RemoteMessageBo messageBo = new RemoteMessageBo();
            messageBo.setRecordId(executor.getExecutor());
            messageBo.setStoreId(patrolExecutor.getStoreId());
            return messageBo;
    }


    private Boolean insertLogic(PatrolTaskBo bo, PatrolTask add) {
        PatrolSubtaskCommon patrolSubtaskCommon = buildPatrolSubtaskCommon(bo);
        if (patrolSubtaskCommonService.insert(patrolSubtaskCommon) > 0) {
            log.info("insertLogic===>新增主子任务成功");
            add.setMainSubtaskId(patrolSubtaskCommon.getId());
            // 创建定时任务，用于检查子任务是否在规定时间内完成，刷新子主任务过期状态
            ResultDTO<Long> result = powerJobClient.saveJob(getMainTaskJobInfoRequest(bo));
            if (result.isSuccess()) {
                log.info("insertLogic===>创建主任务过期状态定时任务成功，任务ID={}", result.getData());
                add.setCycleTaskId(result.getData());
                baseMapper.updateById(add);

                // 判断当前任务结束时间，如果小于当前时间，则只创建主子任务，不创建非子任务、定时任务
                if (patrolSubtaskCommon.getExecuteEndTime() >= System.currentTimeMillis() + 5000) {
                    // 创建定时任务,用于循环周期内非子任务的创建
                    result = powerJobClient.saveJob(getSaveJobInfoRequest(bo));
                    if (result.isSuccess()) {
                        log.info("insertLogic===>创建定时任务成功，任务ID={}", result.getData());
                        if (powerJobClient.runJob(result.getData()).isSuccess()) {
                            log.info("insertLogic===>运行定时任务成功，data={}，message={}", result.getData(), result.getMessage());
                            patrolSubtaskCommon.setCycleTaskId(result.getData());
                            patrolSubtaskCommonService.updateById(patrolSubtaskCommon);
                            log.info("insertLogic===>更新定时任务ids成功");
                            return true;
                        } else {
                            log.warn("insertLogic===>运行定时任务失败，data={}，message={}", result.getData(), result.getMessage());
                            // 删除已经创建的定时任务
                            powerJobClient.deleteJob(result.getData());
                        }
                    }
                    log.warn("insertLogic===>创建定时任务失败，data={}，message={}", result.getData(), result.getMessage());
                } else {
                    log.info("insertLogic===>新增主子任务成功, 创建定时任务失败 {} < 系统当前时间{}", patrolSubtaskCommon.getExecuteEndTime(), System.currentTimeMillis() + 5000);
                    return true;
                }
            }
            throw new TaskException("新增巡店任务失败");
        }
        throw new TaskException("新增实时巡店子任务失败");
    }

    private SaveJobInfoRequest getMainTaskJobInfoRequest(PatrolTaskBo bo) {
        SaveJobInfoRequest request = new SaveJobInfoRequest();
        request.setMinCpuCores(0);
        request.setMinDiskSpace(0);
        request.setMinMemorySpace(0);
        request.setJobName(bo.getName() + "_主任务过期状态刷新");
        request.setJobParams(bo.getTaskId() + "");
        request.setExecuteType(STANDALONE);
        request.setTimeExpressionType(CRON);
        request.setTimeExpression(StringUtils.convertTimeStrToCron(bo.getExecuteRule().getExecuteDateEnd()
            + " " + bo.getExecuteRule().getExecuteTimeEnd()));
        LifeCycle lifeCycle = new LifeCycle();
        lifeCycle.setStart(bo.getExpectStartTime());
        lifeCycle.setEnd(bo.getExpectFinishTime());
        request.setLifeCycle(lifeCycle);
        request.setProcessorType(BUILT_IN);
        request.setConcurrency(Integer.MAX_VALUE);
        request.setProcessorInfo("org.dromara.job.processors.PatrolTaskCheckMainExpiredProcessor");
        return request;
    }

    /**
     * 构建pj定时任务request
     * @param bo
     * @return
     */
    private SaveJobInfoRequest getSaveJobInfoRequest(PatrolTaskBo bo) {
        SaveJobInfoRequest request = new SaveJobInfoRequest();
        request.setMinCpuCores(0);
        request.setMinDiskSpace(0);
        request.setMinMemorySpace(0);
        request.setJobName(bo.getName());
        request.setJobParams(bo.getTaskId() + "");
        request.setExecuteType(STANDALONE);
        request.setTimeExpressionType(CRON);
        switch (bo.getCycleType()) {
            case NO_REPEAT ->
                    request.setTimeExpression(StringUtils.convertTimeStrToCron(bo.getExecuteRule().getExecuteDateEnd()
                            + " " + bo.getExecuteRule().getExecuteTimeEnd()));
            case DAILY -> request.setTimeExpression("0 0 0 1/1 * ? *");
            case WEEKLY -> request.setTimeExpression("0 0 0 * * "
                    + CollUtil.join(bo.getExecuteRule().getWeeklyRule(), ",") + " *");
            case MONTHLY -> request.setTimeExpression("0 0 0 "
                    + CollUtil.join(bo.getExecuteRule().getMonthlyRule(), ",") + " * ?");
            default -> {
            }
        }
        LifeCycle lifeCycle = new LifeCycle();
        lifeCycle.setStart(bo.getExpectStartTime());
        lifeCycle.setEnd(bo.getExpectFinishTime());
        request.setLifeCycle(lifeCycle);
        request.setProcessorType(BUILT_IN);
        request.setConcurrency(Integer.MAX_VALUE);
        request.setProcessorInfo("org.dromara.job.processors.PatrolTaskCircleCreateProcessor");
        return request;
    }

    /**
     * 获取巡店管理-巡店任务（实时、现场、录像，不包含自检任务）配置任务编码
     * @return
     */
    private String getCode() {
        PatrolTask patrolTask = baseMapper.selectOne(new QueryWrapper<>(PatrolTask.class)
            .select("max(code) as code"));
//            .eq("tenant_id", LoginHelper.getTenantId()))
        return StringUtils.generatePatrolTaskCode("XD",
            ObjUtil.isNull(patrolTask) ? null : patrolTask.getCode());
    }

    /**
     * 获取巡店管理-巡店任务（实时、现场、录像，不包含自检任务）配置周期类型
     * @param executeRule
     * @return
     */
    private RepeatType getCycleType(ExecuteRule executeRule) {
        return executeRule.getCycleType();
    }

    /**
     * 获取巡店管理-巡店任务（实时、现场、录像，不包含自检任务）配置预计开始时间
     * @param executeRule
     * @return
     */
    private Long getExpectStartTime(ExecuteRule executeRule) {
        Date date = DateUtils.parseDate(executeRule.getExecuteDateStart()
            +  " " + executeRule.getExecuteTimeStart());
        return date.getTime();
    }

    /**
     * 获取巡店管理-巡店任务（实时、现场、录像，不包含自检任务）配置预计结束时间
     * @param executeRule
     * @return
     */
    private Long getExpectFinishTime(ExecuteRule executeRule) {
        Date date = DateUtils.parseDate(executeRule.getExecuteDateEnd()
            + " " + executeRule.getExecuteTimeEnd());
        return date.getTime();
    }

    /**
     * 构建任务新增数据对象
     * @param bo
     * @return
     */
    private PatrolTask buildPatrolTask(PatrolTaskBo bo) {
        bo.setTenantId(LoginHelper.getTenantId());
        bo.setCreatedTime(System.currentTimeMillis());
        bo.setExpectStartTime(getExpectStartTime(bo.getExecuteRule()));
        bo.setExpectFinishTime(getExpectFinishTime(bo.getExecuteRule()));
        bo.setCycleType(getCycleType(bo.getExecuteRule()));
        bo.setCreator(LoginHelper.getUserId());
        bo.setStatus(TaskFinishStatus.TODO);
        bo.setVer("V1");
        bo.setStoreIds(bo.getPatrolExecutors().stream().map(PatrolExecutors::getStoreId).toList());
        PatrolTask add = MapstructUtils.convert(bo, PatrolTask.class);
        validEntityBeforeSave(add);
        add.setCode(getCode());
        add.setExecutable("1");
        return add;
    }

    /**
     * 构建子任务新增数据
     * @param bo
     * @return
     */
    private PatrolSubtaskCommon buildPatrolSubtaskCommon(PatrolTaskBo bo) {
        PatrolSubtaskCommon add = BeanUtil.copyProperties(bo, PatrolSubtaskCommon.class);
        if (ObjUtil.isNull(add)) {
            throw new TaskException("新增数据不能为空");
        }
        add.setTaskId(bo.getTaskId());
        add.setMainTask(MainTaskType.MAIN.getCode());
        add.setTaskCreatedTime(bo.getCreatedTime());
        add.setTenantId(bo.getTenantId());
        add.setExecuteDateStart(bo.getExecuteRule().getExecuteDateStart());
        add.setExecuteDateEnd(bo.getExecuteRule().getExecuteDateEnd());
        add.setExecuteTimeStart(bo.getExecuteRule().getExecuteTimeStart());
        add.setExecuteTimeEnd(bo.getExecuteRule().getExecuteTimeEnd());
        add.setNotifierIds(bo.getNotifierIds());
        add.setExecuteStartTime(bo.getExpectStartTime());
        add.setExecuteEndTime(bo.getExpectFinishTime());
        add.setExpiredStatus(getExpiredStatus(System.currentTimeMillis(), bo.getExpectFinishTime()));
        return add;
    }

    /**
     * 获取过期状态
     * @param currentTime
     * @param finishTime
     * @return
     */
    private String getExpiredStatus(Long currentTime, Long finishTime) {
        return DateUtils.getTaskExpiredStatusByTime(currentTime, finishTime, 1000 * 60 * 60 * 24L);
    }

    /**
     * 获取巡店ids
     * @param patrolExecutors
     * @return
     */
    private String getStoreIds(List<PatrolExecutors> patrolExecutors) {
        if (CollUtil.isNotEmpty(patrolExecutors)) {
            List<Long> storeIds = patrolExecutors.stream().map(PatrolExecutors::getStoreId).toList();
            return CollUtil.join(storeIds, ",");
        }
        return null;
    }

    /**
     * 修改巡店管理-巡店任务（实时、现场、录像，不包含自检任务）配置
     */
    @Override
    public Boolean updateByBo(PatrolTaskBo bo) {
        PatrolTask update = MapstructUtils.convert(bo, PatrolTask.class);
        validEntityBeforeSave(update);
        // 判断是否是租户管理员，是的话，可编辑所有主任务，否的话只能编辑自己发布的主任务, 判断任务是否进行中，进行中不能编辑、修改
        PatrolSubtaskCommon patrolSubtaskCommon = patrolSubtaskCommonService.queryMainTask(update.getTaskId());
        if (!LoginHelper.isSuperAdmin()
            || !LoginHelper.isTenantAdmin()
            || !Objects.equals(LoginHelper.getUserId(), patrolSubtaskCommon.getCreator())
            || !TaskFinishStatus.TODO.getCode().equals(patrolSubtaskCommon.getStatus())) {
            throw new TaskException("任务状态或权限异常无法修改");
        }
        // 删除原任务
        if(deleteLogic(patrolSubtaskCommon)) {
            // 添加新任务
            return insertLogic(bo, update);
        }
        throw new TaskException("任务状态异常");
    }

    private Boolean deleteLogic(PatrolSubtaskCommon patrolSubtaskCommon) {
        // 通过taskId，获取所有定时任务id
        List<Long> cycleTaskIds = patrolSubtaskCommonMapper.selectObjs(new LambdaQueryWrapper<PatrolSubtaskCommon>()
            .eq(PatrolSubtaskCommon::getTaskId, patrolSubtaskCommon.getTaskId())
            .select(PatrolSubtaskCommon::getCycleTaskId));
        // 删除所有taskId子任务、定时任务
        if (patrolSubtaskCommonMapper.delete(new LambdaQueryWrapper<PatrolSubtaskCommon>()
            .eq(PatrolSubtaskCommon::getTaskId, patrolSubtaskCommon.getTaskId())) > 0){
            cycleTaskIds.stream().filter(Objects::nonNull).forEach(powerJobClient::deleteJob);
            return true;
        }
        throw new TaskException("删除子任务失败");
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(PatrolTask entity){
        if (ObjUtil.isNull(entity)) {
            throw new TaskException("数据异常");
        }
    }

    /**
     * 批量删除巡店管理-巡店任务（实时、现场、录像，不包含自检任务）配置
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 删除巡店管理任务  删除所有子任务、定时任务
     * @param id
     * @return
     */
    @Override
    @GlobalTransactional
    public Boolean deleteWithId(Long id) {
        // 通过任务ID查找taskId
        PatrolSubtaskCommon patrolSubtaskCommon = patrolSubtaskCommonService.queryMainTask(id);
        if (ObjUtil.isNull(patrolSubtaskCommon)) {
            throw new TaskException("任务已删除");
        }
        // 判断是否是租户管理员，是的话，可删除所有主任务，否的话只能删除自己发布的主任务
        if (!LoginHelper.isSuperAdmin()
            || !LoginHelper.isTenantAdmin()
            || !Objects.equals(LoginHelper.getUserId(), patrolSubtaskCommon.getCreator())) {
            throw new TaskException("无权限删除");
        }
        if(deleteLogic(patrolSubtaskCommon)) {
            // 删除任务
            baseMapper.deleteById(id);
            return true;
        }
        throw new TaskException("删除任务失败");
    }
}
